package cn.eonshine.cuba.inf.modular.security.server.impl;

import java.io.IOException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.google.common.base.Joiner;
import com.google.common.collect.Lists;

import cn.eonshine.base.core.util.ToolUtil;
import cn.eonshine.cuba.inf.common.cert.ReadP12Cert;
import cn.eonshine.cuba.inf.common.cert.ReadPubKey;
import cn.eonshine.cuba.inf.common.http.ClientUtil;
import cn.eonshine.cuba.inf.common.http.InfStatus;
import cn.eonshine.cuba.inf.common.http.Message;
import cn.eonshine.cuba.inf.modular.security.server.IKeyService;
import cn.hutool.core.codec.Base64;
import cn.hutool.core.util.CharsetUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.crypto.asymmetric.KeyType;
import cn.hutool.crypto.asymmetric.RSA;
import cn.hutool.http.HttpResponse;
import cn.hutool.json.JSONUtil;

@Service
@Transactional
public class KeyServiceImpl implements IKeyService {

	@Value("${ssl.private.cert}")
	private String priKeyFile;

	@Value("${ssl.private.passwd}")
	private String priKeyPasswd;

	@Value("${ssl.public.cert}")
	private String pubKeyFile;
	
	// 是否HTTPS协议
	@Value("${server.inf.https:false}")
	private boolean isHttps;
	// 服务端地址
	@Value("${server.inf.ipaddr:127.0.0.1}")
	private String serverIp;
	// 服务端端口
	@Value("${server.inf.port:80}")
	private String serverPort;
	
	@Value("${ssl.isAbsolutePath}")
	private boolean isAbsolutePath;
	
	
	@Override
	public String getServerHttpRoot() {
		List<String> urls = Lists.newArrayList();
		if(isHttps) {
			urls.add("https:/");
			if(!"443".equals(serverPort)) {
				urls.add(serverIp+":"+serverPort);
			}else {
				urls.add(serverIp);
			}
		}else {
			urls.add("http:/");
			urls.add(serverIp+":"+serverPort);
		}
		return Joiner.on("/").join(urls);
	}
	
	public String getPubKeyUrl() {
		List<String> urls = Lists.newArrayList();
		if(isHttps) {
			urls.add("https:/");
			if(!"443".equals(serverPort)) {
				urls.add(serverIp+":"+serverPort);
			}else {
				urls.add(serverIp);
			}
		}else {
			urls.add("http:/");
			urls.add(serverIp+":"+serverPort);
		}
		urls.add("securityKey/getPublicKeyBase64");
		return Joiner.on("/").join(urls);
	}
	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * cn.eonshine.cuba.inf.modular.security.server.impl.IKeyService#getKeyFilePath(
	 * )
	 */
	@Override
	public String getLocalPriKeyFilePath() {
		if(isAbsolutePath) {
			return priKeyFile;
		}else {
			return ToolUtil.getWebRootPath(null)+priKeyFile;
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * cn.eonshine.cuba.inf.modular.security.server.IKeyService#getPubKeyFilePath()
	 */
	public String getLocalPubKeyFilePath() {
		if(isAbsolutePath) {
			return pubKeyFile;
		}else {
			return ToolUtil.getWebRootPath(null)+pubKeyFile;
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * cn.eonshine.cuba.inf.modular.security.server.impl.IKeyService#getPubKeyBase64
	 * ()
	 */
	@Override
	public String getLocalPubKeyBase64() throws UnrecoverableKeyException, KeyStoreException, NoSuchAlgorithmException,
			CertificateException, IOException {
		String file = getLocalPubKeyFilePath();
		// KeyStore ks = ReadP12Cert.getKeyStore(file, priKeyPasswd);
		// byte[] publicKeyBase64 = ReadP12Cert.getPublicKey(ks).getEncoded();
		// return Base64.encode(publicKeyBase64);
		return ReadPubKey.getLocalPubKey(file);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * cn.eonshine.cuba.inf.modular.security.server.impl.IKeyService#getPriKeyBase64
	 * ()
	 */
	@Override
	public String getLocalPriKeyBase64() throws KeyStoreException, NoSuchAlgorithmException, CertificateException,
			IOException, UnrecoverableKeyException {
		String file = getLocalPriKeyFilePath();
		KeyStore ks = ReadP12Cert.getKeyStore(file, priKeyPasswd);
		byte[] privateKey = ReadP12Cert.getPrivateKey(ks, priKeyPasswd).getEncoded();
		return Base64.encode(privateKey);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * cn.eonshine.cuba.inf.modular.security.server.IKeyService#getServerEncryptInfo
	 * (java.lang.String, java.lang.String)
	 */
	@Override
	public String getServerEncryptInfo(String json, String clientPubKey) {
		// 用客户端的公钥加密查询结果数据
		RSA rsaClient = SecureUtil.rsa(null, clientPubKey);
		String code = Base64.encode(rsaClient.encrypt(json, KeyType.PublicKey));
		return code;
	}

	/*
	 * (non-Javadoc)
	 * @see cn.eonshine.cuba.inf.modular.security.server.IKeyService#getServerDecryptInfo(java.lang.String)
	 */
	@Override
	public String getServerDecryptInfo(String encryptContent) throws UnrecoverableKeyException, KeyStoreException, NoSuchAlgorithmException, CertificateException, IOException {
		String priKey = getLocalPriKeyBase64();
		// 用客户端的公钥加密查询结果数据
		RSA rsaClient = SecureUtil.rsa(priKey, null);
		String code = StrUtil.str(rsaClient.decryptFromBase64(encryptContent, KeyType.PrivateKey), CharsetUtil.UTF_8);
		return code;
	}
	
	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * cn.eonshine.cuba.inf.modular.security.server.IKeyService#serverEncryptData(
	 * java.lang.String, java.lang.String)
	 */
	@Override
	public Message serverEncryptData(String json, long count, String encryptClientPubKey) {
		Message message = new Message();
		int status = InfStatus.INF_OK;
		String msg = null;
		try {
			if (count > 0) {
				String clientPubKey = getServerDecryptInfo(encryptClientPubKey);
				msg = getServerEncryptInfo(json, clientPubKey);
			}
		} catch (UnrecoverableKeyException e) {
			status = InfStatus.INF_ERROR;
			msg = getErrorMsg(e);
			e.printStackTrace();
		} catch (KeyStoreException e) {
			status = InfStatus.INF_ERROR;
			msg = getErrorMsg(e);
			e.printStackTrace();
		} catch (NoSuchAlgorithmException e) {
			status = InfStatus.INF_ERROR;
			msg = getErrorMsg(e);
			e.printStackTrace();
		} catch (CertificateException e) {
			status = InfStatus.INF_ERROR;
			msg = getErrorMsg(e);
			e.printStackTrace();
		} catch (IOException e) {
			status = InfStatus.INF_ERROR;
			msg = getErrorMsg(e);
			e.printStackTrace();
		}

		message.setStatus(status);
		message.setMsg(msg);
		message.setCount(count);

		return message;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * cn.eonshine.cuba.inf.modular.security.server.IKeyService#requestServerPubKey(
	 * )
	 */
	public Message requestServerPubKey() {

		Message message = new Message();
		int status = InfStatus.INF_OK;
		String msg = null;
		int count = 0;

		String pubKeyUrl = getPubKeyUrl();
		HttpResponse resp1 = ClientUtil.postJsonBodyData(pubKeyUrl, null);
		status = resp1.getStatus();
		// 网络 200
		if (status == 200) {
			message = JSONUtil.toBean(resp1.body(), Message.class);
			System.out.println("server PubKey = " + message);
			count = 1;

		} else {
			status = InfStatus.INF_ERROR;
			msg = resp1.body();
			message.setMsg(msg);
		}

		message.setStatus(status);
		message.setCount(count);

		return message;

	}

	/*
	 * (non-Javadoc)
	 * @see cn.eonshine.cuba.inf.modular.security.server.IKeyService#serverDecrypteData(java.lang.String, java.lang.String)
	 */
	@Override
	public Message serverDecrypteData(String status, String encryptJson, String encryptClientPubKey) {
		Message message = new Message();
		
		
		return null;
	}
	
//=============================================================================================================================	
//	以下属于客户端调用代码
//=============================================================================================================================

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * cn.eonshine.cuba.inf.modular.security.server.IKeyService#clientRequestData()
	 */
	@Override
	public Message clientRequestDataList(String url, String clientParamContent) {
		Message message = new Message();
		int status = InfStatus.INF_OK;
		String msg = null;
		int count = 0;

		try {
			// 1、请求服务端的公钥信息，用于本地客户端证书公钥的加密
			Message serverPubKeyMessage = requestServerPubKey();

			// 2、将本地客户端的公钥证书加密，再作为参数传递给服务端。此公钥证书用于服务端对查询结果数据加密
			if (serverPubKeyMessage.getStatus() == InfStatus.INF_OK) {
				// 获取本地客户端的公钥证书
				String clientPubKey = getLocalPubKeyBase64();
				// 用服务器端的PubKey加密本地客户端公钥证书数据，加密后的数据传输到服务端
				String transClientPubKey = getServerEncryptInfo(clientPubKey, serverPubKeyMessage.getPubKey());
				message.setPubKey(transClientPubKey);
				if(StrUtil.isNotBlank(clientParamContent)) {
					String transMsg = getServerEncryptInfo(clientParamContent, serverPubKeyMessage.getPubKey());
					message.setMsg(transMsg);
				}

				String json = JSONUtil.toJsonStr(message);
				// 发送请求数据，参数为加密后的本地客户端证书的公钥
				HttpResponse resp = ClientUtil.postJsonBodyData(url, json);
				if(resp.getStatus() == InfStatus.INF_OK) {
					// 从服务端获取查询结果数据，查询结果采用客户端公钥进行加密，需要客户端的私钥进行解密后在处理
					String result2 = resp.body();
					Message serverMessage = JSONUtil.toBean(result2, Message.class);
					// 服务器反馈的有数据，则将数据进行解密
					if (serverMessage.getCount() > 0 && StrUtil.isNotBlank(serverMessage.getMsg())) {
						// getClientTransPubKey()
						String encryptContent = serverMessage.getMsg();
						String localPrikeyBase64 = getLocalPriKeyBase64();
						RSA clientRsa = SecureUtil.rsa(localPrikeyBase64, null);
						String decryptContent = StrUtil.str(
								clientRsa.decrypt(Base64.decode(encryptContent), KeyType.PrivateKey), CharsetUtil.UTF_8);
	
						serverMessage.setMsg(decryptContent);
					}
	
					// 请求服务端数据查询的反馈信息
					return serverMessage;
				}else {
					System.out.println("404 网络传输错误");
					System.out.println(resp);
					status = InfStatus.INF_ERROR;
					msg = resp.toString();
				}
			} else {
				// 请求服务器端公钥证书的反馈信息
				return message;
			}
		} catch (UnrecoverableKeyException e) {
			status = InfStatus.INF_ERROR;
			msg = getErrorMsg(e);
			e.printStackTrace();
		} catch (KeyStoreException e) {
			status = InfStatus.INF_ERROR;
			msg = getErrorMsg(e);
			e.printStackTrace();
		} catch (NoSuchAlgorithmException e) {
			status = InfStatus.INF_ERROR;
			msg = getErrorMsg(e);
			e.printStackTrace();
		} catch (CertificateException e) {
			status = InfStatus.INF_ERROR;
			msg = getErrorMsg(e);
			e.printStackTrace();
		} catch (IOException e) {
			status = InfStatus.INF_ERROR;
			msg = getErrorMsg(e);
			e.printStackTrace();
		} catch (Exception e) {
			status = InfStatus.INF_ERROR;
			msg = getErrorMsg(e);
			e.printStackTrace();
		}

		message.setStatus(status);
		message.setMsg(msg);
		message.setCount(count);

		return message;
	}

	private String getErrorMsg(Exception e) {
		return Message.getErrorMsg(e);
	}

	@Override
	public Date timeConversion(String dateStr) {
		dateStr=dateStr.replace("Z", " UTC");//注意是空格+UTC
		SimpleDateFormat form=new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss Z");
		try {
			Date d = form.parse(dateStr);
			return d;
		} catch (ParseException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			return null;
		}
		
	}

}
